/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.wdullaer.materialdatetimepicker.date;

import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.ComponentContainer.LayoutConfig;
import ohos.agp.text.Font;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

import com.wdullaer.materialdatetimepicker.Utils;
import com.wdullaer.materialdatetimepicker.common.RecycleItemProvider2;
import com.wdullaer.materialdatetimepicker.date.MonthView.OnDayClickListener;

import java.util.Calendar;
import java.util.TimeZone;

/**
 * An adapter for a list of {@link MonthView} items.
 */
@SuppressWarnings("WeakerAccess")
public abstract class MonthAdapter extends RecycleItemProvider2 implements OnDayClickListener {

    private static final String TAG = "MonthFragment";
    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 1234567, TAG);

    protected final DatePickerController mController;

    private Font font;

    private CalendarDay mSelectedDay;

    protected static final int MONTHS_IN_YEAR = 12;

    /**
     * A convenience class to represent a specific date.
     */
    public static class CalendarDay {
        private Calendar calendar;
        int year;
        int month;
        int day;
        TimeZone mTimeZone;

        public CalendarDay(TimeZone timeZone) {
            mTimeZone = timeZone;
            setTime(System.currentTimeMillis());
        }

        public CalendarDay(long timeInMillis, TimeZone timeZone) {
            mTimeZone = timeZone;
            setTime(timeInMillis);
        }

        public CalendarDay(Calendar calendar, TimeZone timeZone) {
            mTimeZone = timeZone;
            year = calendar.get(Calendar.YEAR);
            month = calendar.get(Calendar.MONTH);
            day = calendar.get(Calendar.DAY_OF_MONTH);
        }

        @SuppressWarnings("unused")
        public CalendarDay(int year, int month, int day) {
            setDay(year, month, day);
        }

        public CalendarDay(int year, int month, int day, TimeZone timezone) {
            mTimeZone = timezone;
            setDay(year, month, day);
        }

        public void set(CalendarDay date) {
            year = date.year;
            month = date.month;
            day = date.day;
        }

        public void setDay(int year, int month, int day) {
            this.year = year;
            this.month = month;
            this.day = day;
        }

        private void setTime(long timeInMillis) {
            if (calendar == null) {
                calendar = Calendar.getInstance(mTimeZone);
            }
            calendar.setTimeInMillis(timeInMillis);
            month = calendar.get(Calendar.MONTH);
            year = calendar.get(Calendar.YEAR);
            day = calendar.get(Calendar.DAY_OF_MONTH);
        }

        public int getYear() {
            return year;
        }

        public int getMonth() {
            return month;
        }

        public int getDay() {
            return day;
        }
    }

    public MonthAdapter(DatePickerController controller,Font font) {
        mController = controller;
        this.font = font;
        init();
        setSelectedDay(mController.getSelectedDay());
    }

    /**
     * Updates the selected day and related parameters.
     *
     * @param day The day to highlight
     */
    public void setSelectedDay(CalendarDay day) {
        HiLog.warn(LABEL, "### MonthAdapter setSelectedDay(year:" +day.year+", month:"+day.month+", day:"+day.day);
        mSelectedDay = day;
        notifyDataChanged();
    }

    @SuppressWarnings("unused")
    public CalendarDay getSelectedDay() {
        return mSelectedDay;
    }

    /**
     * Set up the gesture detector and selected time
     */
    protected void init() {
        mSelectedDay = new CalendarDay(System.currentTimeMillis(), mController.getTimeZone());
    }

    @Override
    public MonthView getComponent2(int position, Component convertComponent, ComponentContainer parent) {
        HiLog.warn(LABEL, "### MonthAdapter getComponent2 position:"+position);
        MonthView v = (MonthView)convertComponent;
        if (v == null) {
            v = createMonthView(parent.getContext(),font);
        }
        // Set up the new view
        LayoutConfig params = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT);
        v.setLayoutConfig(params);
        v.setClickable(true);
        v.setOnDayClickListener(this);

        bind(v, position, mController, mSelectedDay);
        return v;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override public int getCount() {
        Calendar endDate = mController.getEndDate();
        Calendar startDate = mController.getStartDate();
        int endMonth = endDate.get(Calendar.YEAR) * MONTHS_IN_YEAR + endDate.get(Calendar.MONTH);
        int startMonth = startDate.get(Calendar.YEAR) * MONTHS_IN_YEAR + startDate.get(Calendar.MONTH);

        HiLog.warn(LABEL, "### MonthAdapter getCount count:"+(endMonth - startMonth + 1));
        return endMonth - startMonth + 1;
    }

    public abstract MonthView createMonthView(Context context,Font font);

    @Override
    public void onDayClick(MonthView view, CalendarDay day) {
        if (day != null) {
            Utils.log("MonthAdapter onDayClick year:"+day.year+", month:"+day.month+", day:"+day.day);
            onDayTapped(day);
        }
    }

    /**
     * Maintains the same hour/min/sec but moves the day to the tapped day.
     *
     * @param day The day that was tapped
     */
    protected void onDayTapped(CalendarDay day) {
        Utils.log("MonthAdapter onDayTapped year:"+day.year+", month:"+day.month+", day:"+day.day);
        mController.tryVibrate();
        mController.onDayOfMonthSelected(day.year, day.month, day.day);
        setSelectedDay(day);
    }

    void bind(Component itemView, int position, DatePickerController mController, CalendarDay selectedCalendarDay) {
        HiLog.warn(LABEL, "DayPickerView bind position:"+position+", selectedDay:"+selectedCalendarDay.day);
        final int month = (position + mController.getStartDate().get(Calendar.MONTH)) % MONTHS_IN_YEAR;
        final int year = (position + mController.getStartDate().get(Calendar.MONTH)) / MONTHS_IN_YEAR + mController.getMinYear();

        int selectedDay = -1;
        if (isSelectedDayInMonth(selectedCalendarDay, year, month)) {
            selectedDay = selectedCalendarDay.day;
        }

        ((MonthView) itemView).setMonthParams(selectedDay, year, month, mController.getFirstDayOfWeek());
        itemView.invalidate();
    }

    private boolean isSelectedDayInMonth(CalendarDay selectedDay, int year, int month) {
        return selectedDay.year == year && selectedDay.month == month;
    }

    @Override
    public Object getItem(int position) {
        return mSelectedDay;
    }
}
